package cn.lg.soar.mvc.aspect.log;

import cn.lg.soar.core.aspect.AopEvaluationContext;
import cn.lg.soar.mvc.annotations.Log;
import cn.lg.soar.mvc.aspect.log.model.Config;
import cn.lg.soar.mvc.aspect.log.model.DataConfig;
import cn.lg.soar.mvc.aspect.log.model.ElTextConfig;
import cn.lg.soar.mvc.aspect.log.model.TextConfig;
import cn.lg.soar.mvc.log.ILogHandler;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.util.Map;

/**
 * 操作日志切面
 * @author luguoxiang
 * @date 2022/4/23
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@Aspect
public class LogAspect {

    protected static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private ILogHandler logHandler;

    private final Map<Log, Config> CONFIG_MAP = new ConcurrentReferenceHashMap<>();
    private final static ExpressionParser PARSER = new SpelExpressionParser();

    @Around("@annotation(annotation)")
    public Object around(ProceedingJoinPoint point, Log annotation) throws Throwable {
        Object result = point.proceed();
        try {
            save(annotation, point, result);
        } catch (RuntimeException e) {
            LOGGER.error("记录操作日志失败", e);
        }
        return result;
    }

    /**
     * 保存日志
     * @param point
     * @param point
     * @param result
     */
    private void save(Log annotation, ProceedingJoinPoint point, Object result) {
        Config config = CONFIG_MAP.get(annotation);
        if (config == null) {
            config = getConfig(annotation);
        }

        Object data;
        String dataId;
        if ("".equals(annotation.data())) {
            data = null;
            dataId = "";
        } else {
            if (config instanceof DataConfig) {
                AopEvaluationContext context = new AopEvaluationContext(point, this.applicationContext, result);
                data = config.getData(context);
                dataId = config.getDataId(context).toString();
            } else if (config instanceof ElTextConfig) {
                AopEvaluationContext context = new AopEvaluationContext(point, this.applicationContext, result);
                data = config.getData(context);
                dataId = null;
            } else {
                data = config.getData(null);
                dataId = null;
            }
        }

        if (annotation.sync()) {
            logHandler.logSync(annotation.value(), data, dataId, annotation.dataType());
        } else {
            logHandler.log(annotation.value(), data, dataId, annotation.dataType());
        }
    }

    /**
     * 获取配置
     * @param alog
     * @return
     */
    private synchronized Config getConfig(Log alog) {
        return CONFIG_MAP.computeIfAbsent(alog, a -> {
            String dataId = alog.dataId();
            if (Log.Type.AUTO.equals(alog.type()) && "".equals(dataId) || Log.Type.TEXT.equals(alog.type())) {
                try {
                    Expression data = PARSER.parseExpression(alog.data());
                    return new ElTextConfig(data);
                }catch (RuntimeException e) {
                    return new TextConfig(alog.data());
                }
            }
            Expression data = PARSER.parseExpression(alog.data());
            return new DataConfig(data, PARSER.parseExpression(dataId));
        });
    }
}
